package org.tim.controller;

import java.util.ArrayList;
import net.java.games.input.*;

/**
 *
 * @author Owner
 */
public class Logitech extends Thread {

    /**
     *
     */
    private ArrayList<LogitechControllerListener> listeners = new ArrayList<LogitechControllerListener>();
    /**
     * 
     */
    private Controller c = null;
    /**
     *
     */
    private AutoAnalogUpdater aau = null;

    /**
     *
     * @param c
     * @param l
     */
    public Logitech(Controller c) {
        if (c.getName().equals("Logitech Cordless RumblePad 2 USB")) {
            throw new IllegalArgumentException("Wrong controller " + c.getName());
        }
        this.c = c;
        start();
    }

    /**
     *
     * @param ac
     * @param l
     */
    public Logitech(Controller[] ac) {
        for (int i = 0; i < ac.length; i++) {
            if (ac[i].getName().equals("Logitech Cordless RumblePad 2 USB")) {
                this.c = ac[i];
            }
        }

        if (this.c == null) {
            throw new ControllerNotConnectedException("The \"Logitech cordless RumblePad 2 USB\" controller was not found in your system");
        }

        start();
    }

    /**
     * Add a listener to the listener list
     * @param l The listener
     */
    public void addListener(LogitechControllerListener l) {
        listeners.add(l);
    }

    /**
     * Remove a specific listener from the listener list
     * @param l The listener
     */
    public void removeListener(LogitechControllerListener l) {
        listeners.remove(l);
    }

    /**
     * This run method will poll the components every 100 miliseconds. The analog sticks will not be polled if the autoupdater is on
     * 
     * Available components
     * Name: Z Rotation
     * Name: Z Axis
     * Name: Y Axis
     * Name: X Axis
     * Name: Hat Switch
     * Name: Button 0
     * Name: Button 1
     * Name: Button 2
     * Name: Button 3
     * Name: Button 4
     * Name: Button 5
     * Name: Button 6
     * Name: Button 7
     * Name: Button 8
     * Name: Button 9
     * Name: Button 10
     * Name: Button 11
     */
    @Override
    public void run() {
        c.poll();
        Component[] comps = c.getComponents();
        float[] polldata = new float[17];

        for (int i = 0; i < comps.length; i++) {
            polldata[i] = comps[i].getPollData();
        }

        // Reserve memory for polldata objects
        PollData left;
        PollData right;

        while (true) {
            // Refresh
            c.poll();
            comps = c.getComponents();

            // Loop through all components
            for (int i = 0; i < comps.length; i++) {
                Component comp = comps[i];

                if (comp.getPollData() == 1.0 && polldata[i] == 0.0) {
                    notifyListeners(new ButtonDownEvent(comp));
                } else if (comp.getPollData() == 0.0 && polldata[i] == 1.0) {
                    notifyListeners(new ButtonUpEvent(comp));
                } else if (comp.getPollData() != polldata[i] ) {
                    if (i == 0) {
                        right = new PollData(polldata[1], comp.getPollData(), PollData.RIGHT_ANALOG);
                        notifyListeners(new AnalogEvent(right));
                    } else if (i == 1) {
                        right = new PollData(comp.getPollData(), polldata[0], PollData.RIGHT_ANALOG);
                        notifyListeners(new AnalogEvent(right));
                    } else if (i == 2) {
                        left = new PollData(polldata[3], comp.getPollData(), PollData.LEFT_ANALOG);
                        notifyListeners(new AnalogEvent(left));
                    } else if (i == 3) {
                        left = new PollData(comp.getPollData(), polldata[2], PollData.LEFT_ANALOG);
                        notifyListeners(new AnalogEvent(left));
                    } else {
                        throw new IllegalStateException("Analog event, but not one of both sticks");
                    }
                }

                polldata[i] = comp.getPollData();

            }

            try {
                sleep( 100 );
            } catch (Exception e) {
                // nuthinge
            }
        }
    }

    /**
     * Update the listeners
     */
    public void updateAnalog() {
        c.poll();
        Component[] comps = c.getComponents();
        float[] polldata = new float[17];
        for (int i = 0; i < comps.length; i++) {
            polldata[i] = comps[i].getPollData();
        }
        notifyListeners(new AnalogEvent(new PollData(polldata[1], polldata[0], PollData.RIGHT_ANALOG)));
        notifyListeners(new AnalogEvent(new PollData(polldata[3], polldata[2], PollData.LEFT_ANALOG)));
    }

    /**
     * start the autoupdater
     * @param interval
     */
    public void updateAnalog(long interval) {
        if (interval <= 0 || interval > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("Illegal interval");
        }
        if ( aau == null ) {
            aau = new AutoAnalogUpdater((int) interval);
        } else {
            if ( interval != aau.getInterval() ) {
                interruptAutoUpdate();
                updateAnalog(interval);
            }
        }
    }

    /**
     * Interrupt the auto updater
     */
    public void interruptAutoUpdate() {
        aau.interrupt();
        aau = null;
    }

    /**
     * Do rumble
     * @param force
     */
    public void rumble(float force) {
        c.getRumblers()[0].rumble(force);
    }

    /**
     *
     */
    private class AutoAnalogUpdater extends Thread {

        /**
         *
         */
        private int interval = 1000;

        /**
         *
         * @param interval
         */
        public AutoAnalogUpdater(int interval) {
            this.interval = interval;
            start();
        }

        /**
         *
         */
        @Override
        public void run() {
            while (true) {
                updateAnalog();
                try {
                    sleep(interval);
                } catch (InterruptedException ie) {
                }
            }
        }

        /**
         *
         * @return
         */
        public int getInterval ( ) {
            return interval;
        }
    }

    /**
     *
     * @param ae
     */
    private void notifyListeners(AnalogEvent ae) {
        for (int i = 0; i < listeners.size(); i++) {
            listeners.get(i).analogUpdate(ae);
        }
    }

    /**
     * 
     * @param be
     */
    private void notifyListeners(ButtonEvent be) {
        for (int i = 0; i < listeners.size(); i++) {
            listeners.get(i).buttonPressed(be);
        }
    }
}
